#
# Mesh holds the 3D data
#
# Auther: Wesley Liu
# Email: jinvy.liu@gmail.com
# Date: 12/21/2007
#

require 'matrix'
require File.dirname(__FILE__) + '/AABB'

class Mesh
    attr_accessor :name
    # Array of Vector, every vector contains the coordinates of a vertex
    attr_accessor :vertices
    # Array of Vector, vector n is the indices for face n
    attr_accessor :faces
    # Array of Vector, vector n is the normal for vertex n
    attr_accessor :normals
    # material
    attr_accessor :material
    # texture coordinates
    attr_accessor :texturexys
    
    def initialize
        @vertices = Array.new
        @faces = Array.new
        @normals = Array.new
        @texturexys = Array.new
    end
    
    def instance ins
        @vertices = ins.vertices
        @faces = ins.faces
        @normals = ins.getNormals
        @txtrcrds = ins.texturexys
        @verticespack = ins.verticesPack
        @facespack = ins.facesPack
        @normalspack = ins.normalsPack
        @texturexyspack = ins.texturexysPack
        @aabb = ins.getAABB
    end

    def getNormals
        if @normals.length == 0
            i = 0
            while i < @faces.length do
                # every face has 3 vertex
                vi0 = @faces[i][0]
                vi1 = @faces[i][1]
                vi2 = @faces[i][2]
                v0 = @vertices[vi0]
                v1 = @vertices[vi1]
                v2 = @vertices[vi2]
                # 2 vectors sub
                ab0 = v1[0] - v0[0]
                ab1 = v1[1] - v0[1]
                ab2 = v1[2] - v0[2]
                bc0 = v2[0] - v1[0]
                bc1 = v2[1] - v1[1]
                bc2 = v2[2] - v1[2]
                # cross 2 vectors
                x0 = ab1 * bc2 - ab2 * bc1
                x1 = ab2 * bc0 - ab0 * bc2
                x2 = ab0 * bc1 - ab1 * bc0
                # accumulate normals
                if @normals[vi0] == nil
                    @normals[vi0] = Vector[x0, x1, x2]
                else
                    n = @normals[vi0]
                    @normals[vi0] = Vector[n[0]+x0, n[1]+x1, n[2]+x2]
                end
                if @normals[vi1] == nil
                    @normals[vi1] = Vector[x0, x1, x2]
                else
                    n = @normals[vi1]
                    @normals[vi1] = Vector[n[0]+x0, n[1]+x1, n[2]+x2]
                end
                if @normals[vi2] == nil
                    @normals[vi2] = Vector[x0, x1, x2]
                else
                    n = @normals[vi2]
                    @normals[vi2] = Vector[n[0]+x0, n[1]+x1, n[2]+x2]
                end
                i += 1
            end
            # normalize all vectors
            normalize
        end
        @normals
    end
    
    def normalize
        if @normals.length > 0
            i = 0
            @normals.each do |n|
                if n != nil
                    len = n.r
                    @normals[i] = Vector[n[0]/len, n[1]/len, n[2]/len]
                else
                    @normals[i] = Vector[1.0, 1.0, 1.0]
                end
                i += 1
            end
        end
    end

    def getAABB
        if @aabb == nil
            @aabb = AABB.new self
        end
        @aabb
    end
    
    #Pack format of vertices for GL.VertexPointer, GL.DrawElements and so on
    def verticesPack
        if @verticespack == nil
            verticesarray = Array.new
            @vertices.each do |v|
                v.to_a.each do |i|
                    verticesarray.push i
                end
            end
            @verticespack = verticesarray.pack("f*")
        end
        @verticespack
    end
    
    #Pack format of faces for GL.VertexPointer, GL.DrawElements and so on
    def facesPack
        if @facespack == nil
            facesarray = Array.new
            @faces.each do |f|
                f.to_a.each do |i|
                    facesarray.push i
                end
            end
            @facespack = facesarray.pack("I*")
        end
        @facespack
    end
    
    #Pack format of normals for GL.VertexPointer, GL.DrawElements and so on
    def normalsPack
        if @normalspack == nil
            normalsarray = Array.new
            getNormals.each do |n|
                n.to_a.each do |i|
                    normalsarray.push i
                end
            end
            @normalspack = normalsarray.pack("f*")
        end
        @normalspack
    end
    
    def texturexysPack
        if @texturexyspack == nil
            texturexyarray = Array.new
            texturexys.each do |t|
                t.to_a.each do |i|
                    texturexyarray.push i
                end
            end
            @texturexyspack = texturexyarray.pack("f*")
        end
        @texturexyspack
    end
end

if __FILE__ == $0
    mesh = Mesh.new
    mesh.vertices = [Vector[1.0,0.0,0.0], Vector[0.0,1.0,0.0], Vector[0.0,0.0,1.0], Vector[0.0,0.0,0.0]]
    mesh.faces = [Vector[0,1,2]]
    # vertices
    puts '---------vertices'
    mesh.vertices.each do |v|
        puts "#{v[0]} #{v[1]} #{v[2]}"
    end
    # normals
    puts '---------normals'
    mesh.getNormals.each do |normal|
        puts "#{normal[0]} #{normal[1]} #{normal[2]}"
    end
    # aabb
    puts '---------aabb'
    mesh.getAABB.aabb2v.each do |v|
        puts "#{v[0]} #{v[1]} #{v[2]}"
    end
    puts "verticesPack"
    puts mesh.verticesPack.length
    puts "facesPack"
    puts mesh.facesPack.length
    puts "normalsPack"
    puts mesh.normalsPack.length
end